#include <pthread.h>
#include <time.h> 

#define MAX_LEN 10
#define MAX_PRODUCE_CNT 20

struct Node {
    int val;
    struct Node *pre;
    struct Node *nxt;
};

struct Node *head, *tail;
size_t len = 0;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t wait_for_produce = PTHREAD_COND_INITIALIZER;
pthread_cond_t wait_for_consume = PTHREAD_COND_INITIALIZER;

int consume() {
    int ret;
    struct Node *tmp, *cur;

    cur = head->nxt;
    tmp = cur->nxt;
    
    head->nxt = tmp;
    tmp->pre = head;

    ret = cur->val;
    free(cur);
    
    return ret;
}

void produce(int val) {
    struct Node *tmp, *cur;

    cur = malloc(sizeof(struct Node));
    tmp = tail->pre;

    tmp->nxt = cur;
    cur->pre = tmp;
    cur->nxt = tail;
    tail->pre = cur;

    cur->val = val;
}

void *consumer_run(void *arg) {
    int idx;

    for(;;) {
        pthread_mutex_lock(&lock);
        while (len == 0) {
            pthread_cond_wait(&wait_for_produce, &lock);
        }
        idx = consume();
        len -= 1;
        pthread_mutex_unlock(&lock);
        printf("Consumer consume product %d.\n", idx);
        usleep(1000);
    }
}

void *producer_run(void *arg) {
    int idx;

    for(idx = 0; idx < MAX_PRODUCE_CNT; ++idx) {
        pthread_mutex_lock(&lock);
        while (len == MAX_LEN) {
            pthread_cond_wait(&wait_for_consume, &lock);
        }
        produce(idx);
        len += 1;
        pthread_mutex_unlock(&lock);
        printf("Producer produce product %d.\n", idx);
        usleep(1000);
    }
}

int main(int argc, char *argv[]) {
    head = malloc(sizeof(struct Node));
    tail = malloc(sizeof(struct Node));
    head->nxt = tail;
    tail->pre = head;

    pthread_t consumer, producer;

    pthread_create(&consumer, NULL, consumer_run, NULL);
    pthread_create(&producer, NULL, producer_run, NULL);

    pthread_join(consumer, NULL);
    pthread_join(producer, NULL);

    free(head);
    free(tail);

    return 0;
}